/*
 * @Author: Joel
 * @Date: 2025-04-14 08:47:32
 * @LastEditors: Joel
 * @LastEditTime: 2025-04-16 14:40:23
 * @FilePath: /gd32f303cct6/usr/board_driver/oled.c
 * @Description:
 * Copyright (C) 2025 YiJiaLink, All Rights Reserved.
 * 2025-04-14 08:47:32
 */

#include "oled.h"
unsigned char ssd1315_buffer[8][128];                           //OLED全局缓存
#define ARRAYSIZE 10

/* oled init command */
const uint8_t led_init_command[] =
    {
        0xae, 0x00, 0x10, 0x40, 0x20, 0x02, 0x81, 0xff, 0xa1, 0xc8,
        0xa4, 0xa6, 0xa8, 0x30, 0xd3, 0x00, 0xd5, 0x80, 0xd9, 0xf1,
        0xda, 0x12, 0xdb, 0x40, 0x8d, 0x14};

uint8_t spi0_receive_array[ARRAYSIZE];

static struct oled_func oled_func;

static void oled_init(void);
static void oled_rcu_config(void);
static void oled_gpio_config(void);
static void oled_dma_config(void);
static void oled_spi_config(void);
static void spi_send_command(uint8_t *buf, uint16_t len);
static void spi_send_data(uint8_t *buf, uint16_t len);
static void oled_stop(void);
static void column_set(unsigned char column);
static void page_set(unsigned char page);
static void screen_clear(void);
static void screen_fill(void);
static void setting_dis(void);
static void picture_display(const unsigned char *ptr_pic);
static void ssd1315_gram_draw_point(uint8_t x, uint8_t y);
static void oled_show_buffer(void);
static void ssd1315_gram_draw_line( unsigned char x1, unsigned char y1, unsigned char x2,unsigned char y2);
static void ssd1315_gram_draw_rectangle(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2);
static void ssd1315_gram_draw_circle(unsigned char  x, unsigned char  y, unsigned char  r);
static void oled_draw_bmp(unsigned char x0, unsigned char y0,unsigned char width,unsigned char high,const unsigned char BMP[]);
static void oled_draw_clear(unsigned char x0, unsigned char y0,unsigned char width,unsigned char high, unsigned char color);

/**
* @func:
 * @description: 初始化oled功能结构
 * @return {*}
 * @example:
 */
struct oled_func *oled_driver_init(void)
{
    struct oled_func *oled_fp = &oled_func;

    oled_fp->init_oled = oled_init;
    oled_fp->send_com = spi_send_command;
    oled_fp->send_data = spi_send_data;
    oled_fp->clear_oled = screen_clear;
    oled_fp->fill_oled = screen_fill;
    oled_fp->dis_set = setting_dis;
    oled_fp->pictrue = picture_display;
    oled_fp->stop_oled = oled_stop;
    oled_fp->draw_point = ssd1315_gram_draw_point;
    oled_fp->draw_line = ssd1315_gram_draw_line;
    oled_fp->draw_rectangle = ssd1315_gram_draw_rectangle;
    oled_fp->draw_circle = ssd1315_gram_draw_circle;
    oled_fp->draw_bmp = oled_draw_bmp;
    oled_fp->draw_clear = oled_draw_clear;

    
    return oled_fp;
}
/**
 * @func: 
 * @description: 初始化oled
 * @return {*}
 * @example: 
 */
static void oled_init(void)
{
    /* Reset OLED */
    struct ctrl_func *p_ctl = ctrl_driver_init();
    p_ctl->ctrl_run(OLED_RES, RESET);
    delay_1ms(20);
    p_ctl->ctrl_run(OLED_RES, SET);

    /* enable peripheral clock */
    oled_rcu_config();
    /* configure GPIO */
    oled_gpio_config();
    /* configure DMA */
    oled_dma_config();
    /* configure SPI */
    oled_spi_config();

    /* enable SPI */
    spi_enable(SPI0);

    spi_send_command((uint8_t *)led_init_command, sizeof led_init_command);

    return;
}
/**
 * @func:
 * @description: 关闭发送
 * @return {*}
 * @example:
 */
static void oled_stop(void)
{
    spi_i2s_deinit(SPI0);
    /* disable SPI */
    spi_disable(SPI0);
    return;
}
/**
 * @func:
 * @description: 发送命令
 * @return {*}
 * @example:
 */
static void spi_send_command(uint8_t *buf, uint16_t len)
{
    struct ctrl_func *p_ctl = ctrl_driver_init();
    p_ctl->ctrl_run(OLED_DC, S_COMMAND);
    p_ctl->ctrl_run(OLED_CS, RESET);
    dma_channel_disable(DMA0, DMA_CH2);
    dma_memory_address_config(DMA0, DMA_CH2, (uint32_t)buf);
    dma_transfer_number_config(DMA0, DMA_CH2, len);

    /* spi enable */
    spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);

    // spi_dma_enable(SPI0, SPI_DMA_RECEIVE);

    dma_channel_enable(DMA0, DMA_CH2);

    while (SET == spi_i2s_flag_get(SPI0, SPI_FLAG_TRANS))
    {
    }
    p_ctl->ctrl_run(OLED_DC, SET);
    p_ctl->ctrl_run(OLED_CS, SET);
    return;
}
/**
 * @func:
 * @description: 发送数据
 * @return {*}
 * @example:
 */
static void spi_send_data(uint8_t *buf, uint16_t len)
{
    struct ctrl_func *p_ctl = ctrl_driver_init();
    p_ctl->ctrl_run(OLED_DC, S_DATA);
    p_ctl->ctrl_run(OLED_CS, RESET);
    dma_channel_disable(DMA0, DMA_CH2);
    dma_memory_address_config(DMA0, DMA_CH2, (uint32_t)buf);
    dma_transfer_number_config(DMA0, DMA_CH2, len);

    /* spi enable */
    spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);

    // spi_dma_enable(SPI0, SPI_DMA_RECEIVE);

    dma_channel_enable(DMA0, DMA_CH2);

    while (SET == spi_i2s_flag_get(SPI0, SPI_FLAG_TRANS))
    {
    }
    p_ctl->ctrl_run(OLED_DC, SET);
    p_ctl->ctrl_run(OLED_CS, SET);
    return;
}
/*!
    \brief      configure different peripheral clocks
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void oled_rcu_config(void)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_DMA0);
    rcu_periph_clock_enable(RCU_SPI0);
    rcu_periph_clock_enable(RCU_AF);
    return;
}

/*!
    \brief      configure the GPIO peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void oled_gpio_config(void)
{
    /* 这里只用PA5，PA7 */
    /* configure SPI0 GPIO: SCK/PA5,MOSI/PA7 */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
    /* 这里不要配置nss pa4和 miso pa6 */

    return;
}

/*!
    \brief      configure the DMA peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void oled_dma_config(void)
{
    dma_parameter_struct dma_init_struct;
    dma_struct_para_init(&dma_init_struct);

    /* configure SPI0 transmit DMA: DMA_CH2 */
    dma_deinit(DMA0, DMA_CH2);
    dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
    dma_init_struct.memory_addr = (uint32_t)led_init_command;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
    dma_init_struct.priority = DMA_PRIORITY_LOW;
    dma_init_struct.number = sizeof led_init_command;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init(DMA0, DMA_CH2, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH2);
    dma_memory_to_memory_disable(DMA0, DMA_CH2);

    /* configure SPI0 receive DMA: DMA_CH1 */
    dma_deinit(DMA0, DMA_CH1);
    dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(SPI0);
    dma_init_struct.memory_addr = (uint32_t)spi0_receive_array;
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.priority = DMA_PRIORITY_HIGH;
    dma_init(DMA0, DMA_CH1, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH1);
    dma_memory_to_memory_disable(DMA0, DMA_CH1);

    dma_channel_enable(DMA0, DMA_CH2);
    return;
}

/*!
    \brief      configure the SPI peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void oled_spi_config(void)
{
    spi_parameter_struct spi_init_struct;
    /* deinitialize SPI and the parameters */
    spi_i2s_deinit(SPI0);
    spi_struct_para_init(&spi_init_struct);

    /* configure SPI0 parameter */
    spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode = SPI_MASTER;
    spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
    spi_init_struct.nss = SPI_NSS_SOFT;
    spi_init_struct.prescale = SPI_PSC_256;
    spi_init_struct.endian = SPI_ENDIAN_MSB;
    spi_init(SPI0, &spi_init_struct);

    return;
}
/**
 * @func: 
 * @description: 设置坐标x
 * @param {unsigned char} column
 * @return {*}
 * @example: 
 */
static void column_set(unsigned char column)
{
    uint8_t command = (0x10 | (column >> 4));
    spi_send_command(&command, 1);
    command = (0x00 | (column & 0x0f));
    spi_send_command(&command, 1);

    return;
}
/**
 * @func: 
 * @description: 设置页
 * @param {unsigned char} page
 * @return {*}
 * @example: 
 */
static void page_set(unsigned char page)
{
    uint8_t command = (0xb0 + page);
    spi_send_command(&command, 1);

    return;
}
/**
 * @func: 
 * @description: 清屏
 * @return {*}
 * @example: 
 */
static void screen_clear(void)
{
    unsigned char data = 0x00;
    for (unsigned char page = 0; page < 8; page++) // page loop
    {
        page_set(page);
        column_set(32);
        for (unsigned char column = 0; column < 64; column++) // column loop
        {
            spi_send_data(&data, 1);
        }
    }
    return;
}
/**
 * @func: 
 * @description: 填充
 * @return {*}
 * @example: 
 */
static void screen_fill(void)
{
    unsigned char data = 0xff;
    for (unsigned char page = 0; page < 8; page++) // page loop
    {
        page_set(page);
        column_set(32);
        for (unsigned char column = 0; column < 64; column++) // column loop
        {
            spi_send_data(&data, 1);
        }
    }
    return;
}
/**
 * @func:
 * @description: 显示设置
 * @return {*}
 * @example:
 */
static void setting_dis(void)
{
    unsigned char data[2] = {0xAA, 0x55};
    for (unsigned char j = 0; j < 8; j++)
    {
        page_set(j);
        column_set(32);
        for (unsigned char i = 0; i < 64; i++)
        {
            spi_send_data(data, 2);
        }
    }
    return;
}
/**
 * @func:
 * @description: 图片显示
 * @param {unsigned char} *ptr_pic
 * @return {*}
 * @example:
 */
static void picture_display(const unsigned char *ptr_pic)
{

    for (unsigned char page = 0; page < 8; page++) // page loop
    {
        page_set(page);
        column_set(32);

        spi_send_data((uint8_t *)ptr_pic, 64);
        ptr_pic += 64;
    }
    return;
}
/**
 * @func: 
 * @description: 整屏刷新
 * @return {*}
 * @example: 
 */  
static void oled_show_buffer(void)     
{
    
    uint8_t *pos;
    for(uint8_t i=0;i<8;i++)
    {
       page_set(i);
       column_set(32);
       pos = ssd1315_buffer[i];
       spi_send_data(pos, 64);
       pos+=64;
    }
}
/**
 * @func: 
 * @description: 画点
 * @param {uint8_t} x
 * @param {uint8_t} y
 * @return {*}
 * @example: 
 */
static void ssd1315_gram_draw_point(uint8_t x, uint8_t y)
{
    ssd1315_buffer[y/8][x] |= 1<<(y%8);
    oled_show_buffer();
    return ;                                 /* success return 0 */
}
/**
 * @func: 
 * @description: 画线
 * @param {unsigned char} x1 起点坐标
 * @param {unsigned char} y1 起点坐标
 * @param {unsigned char} x2 终点坐标
 * @param {unsigned char} y2 终点坐标
 * @return {*}
 * @example: 
 */
static void ssd1315_gram_draw_line( unsigned char x1, unsigned char y1, unsigned char x2,unsigned char y2)
{
    unsigned char x,y;
    if( x1>x2 )
    {
        x=x1;x1=x2;x2=x;
        y=y1;y1=y2;y2=y;
    }
    if(x1!=x2)
    {
        for( x = x1; x <= x2; x++ )
        {
            if( y2>y1 )
                ssd1315_gram_draw_point(x, (unsigned char)(y1+(uint16_t)(y2-y1)*(uint16_t)x/(uint16_t)(x2-x1)));
            else
                ssd1315_gram_draw_point(x, (unsigned char)(y1-(uint16_t)(y1-y2)*(uint16_t)x/(uint16_t)(x2-x1)));
        }        
    }
    else
    {
        if( y1>y2 )
        {
            for( y = y2; y <= y1; y++ )
               ssd1315_gram_draw_point(x1, y); 
        }
        else
        {
            for( y = y1; y <= y2; y++ )
               ssd1315_gram_draw_point(x1, y);             
        }
    }
}
/**
 * @func: 
 * @description: 画矩形
 * @param {unsigned char} x1 起点坐标
 * @param {unsigned char} y1 起点坐标
 * @param {unsigned char} x2 终点坐标
 * @param {unsigned char} y2 终点坐标
 * @return {*}
 * @example: 
 */
static void ssd1315_gram_draw_rectangle(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2)
{
    ssd1315_gram_draw_line(x1, y1, x2, y1);
    ssd1315_gram_draw_line(x1, y1, x1, y2);
    ssd1315_gram_draw_line(x1, y2, x2, y2);
    ssd1315_gram_draw_line(x2, y1, x2, y2);
    return;
}
/**
 * @func: 
 * @description: 画圆
 * @param {unsigned char } x 坐标
 * @param {unsigned char } y 坐标
 * @param {unsigned char } r 半径
 * @return {*}
 * @example: 
 */
static void ssd1315_gram_draw_circle(unsigned char  x, unsigned char  y, unsigned char  r)
{
    int a, b;
    int di;
    a = 0;
    b = r;
    di = 3 - (r << 1);       //判断下个点位置的标志
 
    while (a <= b)
    {
        ssd1315_gram_draw_point(x + a, y - b);        //5
        ssd1315_gram_draw_point(x + b, y - a);        //0
        ssd1315_gram_draw_point(x + b, y + a);        //4
        ssd1315_gram_draw_point(x + a, y + b);        //6
        ssd1315_gram_draw_point(x - a, y + b);        //1
        ssd1315_gram_draw_point(x - b, y + a);
        ssd1315_gram_draw_point(x - a, y - b);        //2
        ssd1315_gram_draw_point(x - b, y - a);        //7
        a++;
 
        //使用Bresenham算法画圆
        if (di < 0)di += 4 * a + 6;
        else
        {
            di += 10 + 4 * (a - b);
            b--;
        }
    }
    return;
}
/**
 * @func: 
 * @description: 显示指定图片
 * @param {unsigned char} x0 x0 开始x坐标 0-64
 * @param {unsigned char} y0 y0 开始y坐标 0-48
 * @param {unsigned char} width  0-64
 * @param {unsigned char} high  0-48
 * @param {unsigned char} BMP 图片数组
 * @return {*}
 * @example: 
 */
static void oled_draw_bmp(unsigned char x0, unsigned char y0,unsigned char width,unsigned char high,const unsigned char BMP[])
{ 	

    uint8_t index = (y0 )>> 3;
    uint8_t index1 = (y0 + high) >> 3;
    uint8_t pos = x0+32;
 
    for (; index < index1; index++)
    {
       
        page_set(index);
        column_set(pos);
        spi_send_data((uint8_t *)BMP,width);
        BMP += width;
    }
}
/**
 * @func: 
 * @description: 清除指定区域
 * @param {unsigned char} x0 开始x坐标 0-64
 * @param {unsigned char} y0 开始y坐标 0-48
 * @param {unsigned char} width 0-64
 * @param {unsigned char} high 0-48
 * @param {unsigned char} color 填充颜色 0xff/0x00
 * @return {*}
 * @example: 
 */
static void oled_draw_clear(unsigned char x0, unsigned char y0,unsigned char width,unsigned char high, unsigned char color)
{ 	

    uint8_t index = y0 >> 3;
    uint8_t index1 = (y0 + high) >> 3;
    uint8_t pos = x0+32;
    for (; index < index1; index++)
    {
       
        page_set(index);
        column_set(pos);

        for (unsigned char column = 0; column < width; column++) // column loop
        {
            spi_send_data(&color, 1);
        }
    }
}